#include <iostream>
#include <cassert>
#include "koopa_parse.hpp"

using std::cout;
using std::endl;
using std::ostream;

void KoopaParse(const char *str, ostream &out) {
	// 载入koopa_ir
	koopa_program_t program;
	koopa_error_code_t koopa_ret = koopa_parse_from_string(str, &program);
	assert(koopa_ret == KOOPA_EC_SUCCESS);
	// 创建builder，构建raw program
	koopa_raw_program_builder_t builder = koopa_new_raw_program_builder();
	// 转换
	koopa_raw_program_t raw = koopa_build_raw_program(builder, program);
	// 释放用于载入koopa_ir的内存
	koopa_delete_program(program);

	Visit(raw, out);

	// 释放builder内存
	koopa_delete_raw_program_builder(builder);
}

void Visit(const koopa_raw_program_t &program, ostream &out) {
	out << "  .text" << endl;
	// TODO
	
	Visit(program.values, out);
	Visit(program.funcs, out);
}

void Visit(const koopa_raw_slice_t &slice, ostream &out) {
	// TODO
	
	for (size_t i = 0; i < slice.len; ++i) {
		auto ptr = slice.buffer[i];
		switch (slice.kind) {
			case KOOPA_RSIK_FUNCTION:
				Visit(reinterpret_cast<koopa_raw_function_t>(ptr), out);
				break;
			case KOOPA_RSIK_BASIC_BLOCK:
				Visit(reinterpret_cast<koopa_raw_basic_block_t>(ptr), out);
				break;
			case KOOPA_RSIK_VALUE:
				Visit(reinterpret_cast<koopa_raw_value_t>(ptr), out);
				break;
			default:
				assert(false);
		}
	}
}

void Visit(const koopa_raw_function_t &func, ostream &out) {
	out << "  .global " << func->name+1 << endl;
	out << func->name+1 << ":" << endl;
	// TODO
	
	Visit(func->bbs, out);
}

void Visit(const koopa_raw_value_t &value, ostream &out) {
	// TODO
	
	const auto &kind = value->kind;

	switch(kind.tag) {
		case KOOPA_RVT_RETURN: // return 指令
			Visit(kind.data.ret, out);
			break;
		case KOOPA_RVT_INTEGER: // integer 指令
			// Visit(kind.data.integer, out);
			out << kind.data.integer.value << endl;
			break;
		default:
			assert(false);
	}
}

void Visit(const koopa_raw_basic_block_t &bb, ostream &out) {
	// TODO
	Visit(bb->insts, out);
}

void Visit(const koopa_raw_return_t &ret, ostream &out) {
	koopa_raw_value_t ret_value = ret.value;
	assert(ret_value->kind.tag == KOOPA_RVT_INTEGER);
	int32_t int_val = ret_value->kind.data.integer.value;
	out << "  li a0, " << int_val << endl;
	out << "  ret" << endl;
}
